完整的故障转移测试
测试准备
启动所有服务:
pnpm dev:gateway # 网关服务
pnpm dev:user # 微服务实例 (40001)
pnpm dev:user1 # 微服务实例 (40002)
pnpm dev:user2 # 微服务实例 (40003, DC2)
pnpm dev:health # 健康检查服务
bash
测试 1:正常请求
curl http://localhost:3040/api/v1/auth/login
# 预期:成功返回 access token
bash
测试 2:单实例故障转移
# 停止 40002 实例
# 再次发起请求
curl http://localhost:3040/api/v1/auth/login
# 预期:请求通过拦截器重试后,使用其他健康实例成功返回
bash
关键观察:
- 错误首先被
promisifyServiceMethod的 catch 捕获(优先级高于拦截器) errorCallbackFunction触发updateGrpcClient- 从 Consul 获取新的健康实例
- BehaviorSubject 发布新客户端
- 后续请求自动使用新实例
测试 3:多实例故障
# 停止所有 dc1 实例 (40001, 40002)
# 请求需要跨数据中心转移到 dc2 的 40003
curl http://localhost:3040/api/v1/auth/login
# 预期:延迟后返回结果(需要跨数据中心查询)
bash
错误处理的优先级
整个错误处理链路有明确的优先级:
promisifyServiceMethod (catch)
↓ 优先捕获,触发 updateGrpcClient
↓ throw error 继续抛出
GrpcRetryInterceptor (catchError)
↓ 延迟 + 重试 3 次
↓ 最终失败抛出 "Service update timeout"
客户端收到 500 错误
text
promisifyServiceMethod 的优先级高于拦截器,因为它是更靠近错误源的捕获点。
高可用架构总结
核心组件
| 组件 | 职责 |
|---|---|
| ConsulCoreModule | 统一管理所有 Consul 实例和 gRPC 客户端 |
| ConsulService | 服务发现、健康检查、客户端更新 |
| GrpcRetryInterceptor | 请求级重试,指数退避 |
| promisifyServiceMethod | 方法级错误捕获和回调 |
| GrpcClientInterface | 完整的客户端上下文信息 |
设计模式
- BehaviorSubject 模式:gRPC 客户端通过 BehaviorSubject 管理,实现响应式更新
- 静态 Map 模式:ConsulCoreModule 通过静态变量跨模块共享实例
- 回调模式:promisifyServiceMethod 通过 error callback 暴露异常
- 重试模式:拦截器实现指数退避重试
- 故障转移模式:updateGrpcClient 自动切换到健康实例
CAP 理论在 Consul 中的体现
Consul 遵循 CP 模型(一致性 + 分区容忍性),这意味着:
- 优点:数据一致性强,不会出现脑裂
- 缺点:可用性不是最高优先级,健康检查有延迟
- 影响:故障转移时可能有 1-2 秒的感知延迟
完整请求链路
客户端请求
→ Gateway (NestJS)
→ AuthService
→ promisifyServiceMethod 包装的 gRPC 调用
→ Consul 服务发现
→ gRPC 微服务实例
← 响应返回
← 返回给客户端
text
后续优化方向
- 跨数据中心自动切换:当 dc1 所有实例不可用时,自动查询 dc2
- 负载均衡策略:从随机选择升级为加权轮询或最少连接数
- 熔断器模式:使用 Circuit Breaker 防止级联故障
- 监控告警:接入 Prometheus + Grafana 监控微服务健康状态
- 配置化重试策略:将重试次数、延迟时间等参数提取为配置项
↑